#pragma once

// class declarations
class CogStopWatch;
class CogMessage; 
class CogMessageLog;
class CogArgument;

// -----

// C argument handling
#include <stdarg.h>
#include <varargs.h>
#pragma warning(disable : 4996) // for vsprintf

// Boost
#include <boost/thread.hpp> // threads
#include <boost/thread/tss.hpp> // thread specific pointer
#include <boost/thread/mutex.hpp> // mutexes

// STL
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <stack>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <map>
#include <ctime>

using namespace std;

// ------------------------------------------------------------------------------------------
// random number generation

#define DSFMT_DO_NOT_USE_OLD_NAMES // Mersenne twister
extern "C" {
#include "Random/SFMT.h" // note: also defines uint32_t and uint64_t
}

inline
void cogSeedRandom(uint32_t seed) 
{ 
	init_gen_rand(seed); 
}

inline
double cogRandomDouble() 
{ 
	return genrand_real2(); 
}

inline
uint32_t cogRandomInteger() 
{ 
	return gen_rand32();
}

inline
size_t cogRandomIndex(size_t limit) 
{ 
	return gen_rand32() % limit; 
}

// ------------------------------------------------------------------------------------------
// string processing

string toLower(const string& label);

// ------------------------------------------------------------------------------------------
// time measurement

class CogStopWatch 
{
public:
    CogStopWatch();
    void startTimer();
    void stopTimer();
    double getElapsedTime();

private: 
	uint64_t start;
	uint64_t stop;
};

// ------------------------------------------------------------------------------------------
// message logging

extern CogMessageLog globalMessageLog; // global var

// -----

typedef uint32_t CogMessageType;
const CogMessageType COGMT_NONE = 0x00000000;
const CogMessageType COGMT_INFO_UNDEFINED = 0x00000001;
const CogMessageType COGMT_INFO_IO = 0x00000002;
const CogMessageType COGMT_INFO_SYSTEM = 0x00000004;
const CogMessageType COGMT_INFO_PARAMETER = 0x00000008;
const CogMessageType COGMT_INFO_OBJECTIVE = 0x00000010;
const CogMessageType COGMT_INFO_STATS = 0x00000020;
const CogMessageType COGMT_INFO_ANY = 0x0000FFFF;
const CogMessageType COGMT_ERROR_UNDEFINED = 0x00010000;
const CogMessageType COGMT_ERROR_IO = 0x00020000;
const CogMessageType COGMT_ERROR_SYSTEM = 0x00040000;
const CogMessageType COGMT_ERROR_PARAMETER = 0x00080000;
const CogMessageType COGMT_ERROR_OBJECTIVE = 0x00100000;
const CogMessageType COGMT_ERROR_ANY = 0xFFFF0000;
const CogMessageType COGMT_ANY = 0xFFFFFFFF;

// -----

typedef uint32_t CogMessageVerbosity;
const CogMessageVerbosity VERBOSITY_NONE = 0;
const CogMessageVerbosity VERBOSITY_LOW = 1;
const CogMessageVerbosity VERBOSITY_NORMAL = 2;
const CogMessageVerbosity VERBOSITY_HIGH = 3;
const CogMessageVerbosity VERBOSITY_MAX = 4;

// -----

class CogMessage
{
public:
	CogMessage(CogMessageType _type, CogMessageVerbosity _verbosity, double _time, const string& _text)
	: type(_type),
	  verbosity(_verbosity),
	  time(_time),
	  text(_text)
	{
	}

	// data:
	CogMessageType type;
	CogMessageVerbosity verbosity;
	double time;
	string text;
};

// -----

class CogMessageLog
{
public:
	CogMessageLog() : minVerbose(VERBOSITY_NONE), instantDump(false) { globalTimer.startTimer(); }

	// inline methods:
	void printStream(CogMessageType type, CogMessageVerbosity verbosity, ostringstream& strStream);
	void print(CogMessageType type, CogMessageVerbosity verbosity, const char* text, ...);

	void setMinVerbosity(CogMessageVerbosity verbose) { minVerbose = verbose; }
	void setInstantDump(bool dump = true) { instantDump = dump; }
	double getTime() { globalTimer.stopTimer(); return globalTimer.getElapsedTime(); }

	void show(ostream &out = cout, CogMessageType type = COGMT_ANY, size_t minShowImport = 0);
	void dump(ostream &out = cout, CogMessageType type = COGMT_ANY);
	void clear();
	void clear(CogMessageType type);

	size_t length() const { return messageList.size(); }
	size_t length(CogMessageType type);

private:
	// data:
	boost::mutex messageMutex;
	list<CogMessage> messageList;
	CogMessageVerbosity minVerbose;
	CogStopWatch globalTimer;
	bool instantDump;
};

// ------------------------------------------------------------------------------------------

typedef uint32_t CogArgumentType;
const CogArgumentType COGARG_NONE = 0x00000000;
const CogArgumentType COGARG_FILE_INPUT = 0x00000001;
const CogArgumentType COGARG_FILE_OUTPUT = 0x00000002;
const CogArgumentType COGARG_OUTPUT_PARAMETERS = 0x00000010;
const CogArgumentType COGARG_OUTPUT_CANDIDATES = 0x00000020;
const CogArgumentType COGARG_OUTPUT_TEMPLATES = 0x00000040;
const CogArgumentType COGARG_OUTPUT_RESULTS = 0x00000100;
const CogArgumentType COGARG_OUTPUT_GENERATIONS = 0x00000200;
const CogArgumentType COGARG_OUTPUT_ALLTYPES = 0x00000FF0;
const CogArgumentType COGARG_PARAMETER = 0x00001000;
const CogArgumentType COGARG_STDOUT = 0x00010000;
const CogArgumentType COGARG_ANY = 0xFFFFFFFF;

class CogArgument
{
public:
	CogArgument() : type(COGARG_NONE) {}
	CogArgument(CogArgumentType aType) : type(aType) {}

	// data:
	list<string> valueList;
	CogArgumentType type;
};

// ------------------------------------------------------------------------------------------
// inlined:

inline
void CogMessageLog::printStream(CogMessageType type, CogMessageVerbosity verbose, ostringstream& strStream)
{
	messageMutex.lock();
	messageList.push_back(CogMessage(type, verbose, getTime(), strStream.str()));
	messageMutex.unlock();
	if (instantDump)
	{
		dump(cout);
	}
}

inline
void CogMessageLog::print(CogMessageType type, CogMessageVerbosity verbose, const char* text, ...)
{
	if (verbose <= minVerbose)
	{
		char szBuffer[1024];
		va_list args;
		va_start(args, text);
		vsprintf(szBuffer, text, args);
		va_end(args);
		szBuffer[1023] = '\0';
		messageMutex.lock();
		messageList.push_back(CogMessage(type, verbose, getTime(), szBuffer));
		messageMutex.unlock();
		if (instantDump)
		{
			dump(cout);
		}
	}
}

// ------------------------------------------------------------------------------------------